<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <!--edge浏览器H5兼容设置-->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!--360浏览器H5兼容设置-->
    <meta name="renderer" content="webkit"/>
    <title>CC商城</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!--导入核心文件-->
    <script src="bootstrap3/js/holder.js"></script>
    <link href="bootstrap3/css/bootstrap.css" rel="stylesheet"
          type="text/css">
    <script src="bootstrap3/jquery-1.9.1.min.js"></script>
    <script src="bootstrap3/js/bootstrap.js"></script>
    <!-- 字体图标 -->	<link rel="icon" type="image/x-icon" href="images/favicon.ico">
    <link rel="stylesheet"
          href="bootstrap3/font-awesome-4.7.0/css/font-awesome.css"/>
    <link rel="stylesheet" type="text/css" href="css/top.css"/>
    <link rel="stylesheet" type="text/css" href="css/index.css"/>
    <link rel="stylesheet" type="text/css" href="css/layout.css"/>
    <link rel="stylesheet" type="text/css" href="css/reg.css"/>
    <link rel="stylesheet" type="text/css" href="css/footer.css"/>
    <style type="text/css">
        .c2 {
            margin: 0 auto;
            width: 50%;
            text-align: center;
        }
    </style>
</head>
<body>
<!--头部-->
<header class="header">
    <a href="index.html">
        <img src="https://cdn.jsdelivr.net/gh/elicscc/ccstore/src/main/resources/static/images/index/stumalllogo.png"/>
    </a><span
        class="pull-right"><small>欢迎访问</small><a href="index.html"><b>CC商城</b></a></span>
</header>
<!--主体-->
<div class="c2">
    <!--透明层 -->
    <div>
        <h1>新用户注册</h1>
        <!--表单开始-->
        <form id="registerForm" class="form-horizontal el-register-form" action="/users/reg" method="post">
            <p class="h4" style="margin: 10px 10px 20px;color:#999;">请填写注册信息，点击“提交注册”即可完成注册！</p>
            <div class="form-group">
                <label class="control-label col-sm-2">用户名</label>
                <div class="col-sm-10">
                    <input type="text" autocomplete="off" name="username" class="form-control" id="username"/>
                    <p class="help-block">用户名为4~16位字母，数字，符号或中文</p>
                </div>
            </div>
            <div class="form-group">
                <label class="control-label col-sm-2">密&emsp;码</label>
                <div class="col-sm-10">
                    <input type="password" autocomplete="off" name="password" id="password" class="form-control"/>
                    <p class="help-block">密码为4~16位字符组成,采用数字、字母、符号安全性更高</p>
                </div>
            </div>
            <div class="form-group">
                <label class="control-label col-sm-2">确认密码</label>
                <div class="col-sm-10">
                    <input type="password" autocomplete="off" name="confirmPwd" class="form-control"/>
                    <p class="help-block">请再次填写密码</p>
                </div>
            </div>
            <div class="form-gorup">
                <div class="col-sm-offset-2">
                    <button type="submit" class="btn btn-success">
                        同意协议并注册
                    </button>
                    &emsp;&emsp;
                    <a href="/static/login.html" class="text-primary">已有账号，马上登录</a>

                    <p style="padding-left: 50px;margin-top: 15px;">
                        <a href="#">《使用协议说明书》</a>
                    </p>
                </div>
            </div>
        </form>
    </div>
</div>
<!--页脚开始-->
<footer class="footer">
    <!-- 品质保障，私人定制等-->
    <div class="text-center rights container">
        <div class="col-md-offset-2 col-md-2">
            <span class="fa fa-thumbs-o-up"></span>
            <p>品质保障</p>
        </div>
        <div class="col-md-2">
            <span class="fa fa-address-card-o"></span>
            <p>私人订制</p>
        </div>
        <div class="col-md-2">
            <span class="fa fa-graduation-cap"></span>
            <p>学生特供</p>
        </div>
        <div class="col-md-2">
            <span class="fa fa-bitcoin"></span>
            <p>专属特权</p>
        </div>
    </div>
    <!--联系我们、下载客户端等-->
    <div class="container beforeBottom">
        <div class="col-md-offset-1 col-md-3">
            <div>
                <img src="https://cdn.jsdelivr.net/gh/elicscc/ccstore/src/main/resources/static/images/index/stumalllogo.png" alt=""
                     class="footLogo"/>
            </div>
            <div>
                <img src="https://cdn.jsdelivr.net/gh/elicscc/ccstore/src/main/resources/static/images/index/footerFont.png" alt=""/>
            </div>
        </div>
        <div class="col-md-4 callus text-center">
            <div class="col-md-4">
                <ul>
                    <li><a href="#"><p>买家帮助</p></a></li>
                    <li><a href="#">新手指南</a></li>
                    <li><a href="#">服务保障</a></li>
                    <li><a href="#">常见问题</a></li>
                </ul>
            </div>
            <div class="col-md-4">
                <ul>
                    <li><a href="#"><p>商家帮助</p></a></li>
                    <li><a href="#">商家入驻</a></li>
                    <li><a href="#">商家后台</a></li>
                </ul>
            </div>
            <div class="col-md-4">
                <ul>
                    <li><a href="#"><p>关于我们</p></a></li>
                    <li><a href="#">关于CC</a></li>
                    <li><a href="#">联系我们</a></li>
                    <li><span class="fa fa-wechat"></span> <span
                            class="fa fa-weibo"></span></li>
                </ul>
            </div>
        </div>
        <div class="col-md-4">
            <div class="col-md-5">
                <p>商城客户端</p>
                <img src="https://cdn.jsdelivr.net/gh/elicscc/ccstore/src/main/resources/static/images/index/ios.png" class="lf"> <img
                    src="https://cdn.jsdelivr.net/gh/elicscc/ccstore/src/main/resources/static/images/index/android.png" alt="" class="lf"/>
            </div>
            <div class="col-md-6">
                <img src="https://cdn.jsdelivr.net/gh/elicscc/ccstore/src/main/resources/static/images/index/erweima.png">
            </div>
        </div>
    </div>
    <!-- 页面底部-备案号 #footer -->
    <div class="col-md-12 text-center bottom">
        皖ICP备19024863号
    </div>
</footer>
<!--页脚结束-->
<script type="text/javascript">
    /**
     * jquery.bootstrap.js
     Copyright (c) Kris Zhang <kris.newghost@gmail.com>
     License: MIT (https://github.com/newghost/bootstrap-jquery-plugin/blob/master/LICENSE)
     */
    String.prototype.format || (String.prototype.format = function () {
        var e = arguments;
        return this.replace(/{(\d+)}/g, function (t, n) {
            return typeof e[n] != "undefined" ? e[n] : t
        })
    }), function (e) {
        e.fn.dialog = function (t) {
            var n = this, r = e(n), i = e(document.body), s = r.closest(".dialog"), o = "dialog-parent",
                u = arguments[1], a = arguments[2], f = function () {
                    var t = '<div class="dialog modal fade"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close">&times;</button><h4 class="modal-title"></h4></div><div class="modal-body"></div><div class="modal-footer"></div></div></div></div>';
                    s = e(t), e(document.body).append(s), s.find(".modal-body").append(r)
                }, l = function (r) {
                    var i = (r || t || {}).buttons || {}, o = s.find(".modal-footer");
                    o.empty();
                    var u = i.constructor == Array;
                    for (var a in i) {
                        var f = i[a], l = "", c = "", h = "btn-default", p = "";
                        if (f.constructor == Object) l = f.id, c = f.text, h = f["class"] || f.classed || h, p = f.click; else {
                            if (!!u || f.constructor != Function) continue;
                            c = a, p = f
                        }
                        $button = e('<button type="button" class="btn">').addClass(h).html(c), l && $button.attr("id", l), p && function (e) {
                            $button.click(function () {
                                e.call(n)
                            })
                        }(p), o.append($button)
                    }
                    o.data("buttons", i)
                }, c = function () {
                    s.modal("show")
                }, h = function (e) {
                    s.modal("hide").one("hidden.bs.modal", function () {
                        e && (r.data(o).append(r), s.remove())
                    })
                };
            t.constructor == Object && (!r.data(o) && r.data(o, r.parent()), s.size() < 1 && f(), l(), e(".modal-title", s).html(t.title || ""), e(".modal-dialog", s).addClass(t.dialogClass || ""), e(".modal-header .close", s).click(function () {
                var e = t.onClose || h;
                e.call(n)
            }), (t["class"] || t.classed) && s.addClass(t["class"] || t.classed), t.autoOpen !== !1 && c()), t == "destroy" && h(!0), t == "close" && h(), t == "open" && c();
            if (t == "option" && u == "buttons") {
                if (!a) return s.find(".modal-footer").data("buttons");
                l({buttons: a}), c()
            }
            return n
        }
    }(jQuery), $.messager = function () {
        var e = function (e, t) {
                var n = $.messager.model;
                arguments.length < 2 && (t = e || "", e = "&nbsp;"), $("<div>" + t + "</div>").dialog({
                    title: e,
                    onClose: function () {
                        $(this).dialog("destroy")
                    },
                    buttons: [{
                        text: n.ok.text, classed: n.ok.classed || "btn-success", click: function () {
                            $(this).dialog("destroy")
                        }
                    }]
                })
            }, t = function (e, t, n) {
                var r = $.messager.model;
                $("<div>" + t + "</div>").dialog({
                    title: e, onClose: function () {
                        $(this).dialog("destroy")
                    }, buttons: [{
                        text: r.ok.text, classed: r.ok.classed || "btn-success", click: function () {
                            $(this).dialog("destroy"), n && n()
                        }
                    }, {
                        text: r.cancel.text, classed: r.cancel.classed || "btn-danger", click: function () {
                            $(this).dialog("destroy")
                        }
                    }]
                })
            },
            n = '<div class="dialog modal fade msg-popup"><div class="modal-dialog modal-sm"><div class="modal-content"><div class="modal-body text-center"></div></div></div></div>',
            r, i, s = function (e) {
                r || (r = $(n), $("body").append(r)), r.find(".modal-body").html(e), r.modal({
                    show: !0,
                    backdrop: !1
                }), clearTimeout(i), i = setTimeout(function () {
                    r.modal("hide")
                }, 1500)
            };
        return {alert: e, popup: s, confirm: t}
    }(), $.messager.model = {
        ok: {text: "OK", classed: "btn-success"},
        cancel: {text: "Cancel", classed: "btn-danger"}
    }, function (e) {
        e.fn.datagrid = function (t, n) {
            var r = this, i = e(r), s = i.data("config") || {}, o = i.data("rows") || [],
                u = s.selectedClass || "success", a = s.singleSelect, f = function (t) {
                    var n = s.selectChange, r = s.edit, f = function (t) {
                        var r = e(this), s = r.hasClass(u), f = e("tbody tr", i).index(r), l = o[f] || {};
                        a && e("tbody tr", i).removeClass(u), r.toggleClass(u), n && n(!s, f, l, r)
                    };
                    (n || typeof a != "undefined") && t.click(f);
                    var l = function (t) {
                        var n = e(this), r = n.closest("tr"), s = e("tbody tr", i).index(r), u = o[s] || {},
                            a = n.attr("name");
                        a && (u[a] = n.val())
                    };
                    r && t.find("input").keyup(l)
                }, l = function (e, t) {
                    var n = "<tr>";
                    for (var r = 0, i = e[0].length; r < i; r++) {
                        var o = e[0][r], u = o.formatter, a = o.field, f = o.tip, l = t[a], c = o.maxlength, h = o.readonly;
                        typeof l == "undefined" && (l = ""), s.edit && (c = c ? ' maxlength="{0}"'.format(o.maxlength) : "", h = h ? ' readonly="readonly"' : "", l = '<input name="{0}" value="{1}" class="form-control"{2}{3}/>'.format(o.field, l, c, h)), l = u ? u(t[a], t) : l, n = n + "<td>" + l + "</td>"
                    }
                    return n += "</tr>", n
                }, c = function (t) {
                    if (!n) return;
                    var r = s.columns, o = n.rows || n, u = "<tbody>";
                    if (o) for (var a = 0, c = o.length; a < c; a++) u += l(r, o[a]);
                    u += "</tbody>", e("tbody", i).remove(), i.data("rows", o).append(u), s.edit && i.addClass("edit"), f(e("tbody tr", i))
                }, h = function () {
                    if (n && typeof n.index != "undefined") return [n.index];
                    var t = [];
                    return i.find("tbody tr").each(function (n) {
                        var r = e(this);
                        r.hasClass(u) && t.push(n)
                    }), t
                };
            if (t && t.constructor == Object) {
                var p = t.columns;
                if (p) {
                    e("thead", i).size() < 1 && i.append("<thead></thead>");
                    var d = "<tr>";
                    for (var v = 0, m = p[0].length; v < m; v++) {
                        var g = p[0][v];
                        d += "<th>" + (g.title || "") + "</th>"
                    }
                    d += "</tr>", i.data("config", t), e("thead", i).html(d)
                }
            }
            t == "loadData" && c();
            if (t == "getData") return o;
            if (t == "getConfig") return s;
            if (t == "getColumns") return s.columns;
            if (t == "selectRow") {
                if (typeof a == "undefined") return;
                typeof n == "number" ? (a && i.datagrid("unselectRow"), e("tbody tr", i).eq(n).addClass(u)) : a || e("tbody tr", i).addClass(u)
            }
            t == "unselectRow" && (typeof n != "undefined" ? e("tbody tr", i).eq(n).removeClass(u) : e("tbody tr", i).removeClass(u));
            if (t == "updateRow") {
                var y = h(), b = n.row, p = s.columns;
                for (var v = 0, m = y.length; v < m; v++) {
                    var w = y[v];
                    o && (b = e.extend(o[w], b));
                    var E = e(l(p, b, s));
                    typeof n.index == "undefined" && E.addClass(u), e("tbody tr", i).eq(w).after(E).remove(), f(E)
                }
            }
            if (t == "getSelections") {
                var S = [];
                return e("tbody tr", i).each(function (t) {
                    e(this).hasClass(u) && S.push(o[t])
                }), S
            }
            if (t == "getSelectedIndex") return h();
            if (t == "insertRow") {
                var x = h()[0], b = n.row;
                if (typeof x == "undefined" || x < 0) x = o.length;
                if (!s || !b) return i;
                var T = e("tbody tr", i), E = e(l(s.columns, b, s)), N = T.eq(x);
                f(E), N.size() ? N.before(E) : e("tbody", i).append(E), o.splice(x, 0, b)
            }
            if (t == "deleteRow") {
                var y = typeof n == "number" ? [n] : h();
                for (var v = y.length - 1; v > -1; v--) {
                    var x = y[v];
                    e("tbody tr", i).eq(x).remove(), o.splice(x, 1)
                }
            }
            return r
        }
    }(jQuery), function (e) {
        e.fn.tree = function (t, n) {
            var r = this, i = e(r), s = Array.prototype.push, o = "glyphicon-file", u = "glyphicon-folder-open",
                a = "glyphicon-folder-close", f = function (e, t, n) {
                    var r = [];
                    !t && r.push('<ul style="display:{0}">'.format(n == "close" ? "none" : "block"));
                    for (var i = 0, l = e.length; i < l; i++) {
                        var c = e[i], h = c.children, p = c.id, d = c.state, v = c.attributes;
                        r.push("<li>");
                        var m = typeof h == "undefined" ? o : d == "close" ? a : u;
                        r.push('<span class="glyphicon {0}"></span> '.format(m)), r.push("<a{1}{2}{3}>{0}</a>".format(c.text, h ? " class='tree-node'" : "", p ? " data-id='{0}'".format(p) : "", v ? " data-attr='{0}'".format(JSON.stringify(v)) : "")), h && s.apply(r, f(h, !1, d)), r.push("</li>")
                    }
                    return !t && r.push("</ul>"), r
                }, l = function () {
                    e("span.glyphicon-folder-open, span.glyphicon-folder-close", i).click(function (t) {
                        var n = e(this), r = n.closest("li").children("ul");
                        n.hasClass(a) ? (n.removeClass(a).addClass(u), r.show()) : (n.removeClass(u).addClass(a), r.hide())
                    })
                };
            if (t && t.constructor == Object) {
                var c = t.data;
                if (c && c.constructor == Array) {
                    var h = f(c, !0);
                    i.html(h.join("")), i.data("config", t), l()
                }
                var p = t.onClick;
                p && e("li>a", i).click(function () {
                    var t = e(this);
                    attrs = t.attr("data-attr"), p.call(r, {
                        id: t.attr("data-id"),
                        attributes: attrs ? JSON.parse(attrs) : {},
                        text: t.text()
                    }, t)
                })
            }
            return r
        }
    }(jQuery);
    $.messager.model.ok.text = "确定";
    $.messager.model.cancel.text = "取消";
    /*!
   * jQuery Validation Plugin 1.12.0pre
   *
   * http://bassistance.de/jquery-plugins/jquery-plugin-validation/
   * http://docs.jquery.com/Plugins/Validation
   *
   * Copyright 2013 Jörn Zaefferer
   * Released under the MIT license:
   *   http://www.opensource.org/licenses/mit-license.php
   */

    (function ($) {

        $.extend($.fn, {
            // http://docs.jquery.com/Plugins/Validation/validate
            validate: function (options) {

                // if nothing is selected, return nothing; can't chain anyway
                if (!this.length) {
                    if (options && options.debug && window.console) {
                        console.warn("Nothing selected, can't validate, returning nothing.");
                    }
                    return;
                }

                // check if a validator for this form was already created
                var validator = $.data(this[0], "validator");
                if (validator) {
                    return validator;
                }

                // Add novalidate tag if HTML5.
                this.attr("novalidate", "novalidate");

                validator = new $.validator(options, this[0]);
                $.data(this[0], "validator", validator);

                if (validator.settings.onsubmit) {

                    this.validateDelegate(":submit", "click", function (event) {
                        if (validator.settings.submitHandler) {
                            validator.submitButton = event.target;
                        }
                        // allow suppressing validation by adding a cancel class to the submit button
                        if ($(event.target).hasClass("cancel")) {
                            validator.cancelSubmit = true;
                        }

                        // allow suppressing validation by adding the html5 formnovalidate attribute to the submit button
                        if ($(event.target).attr("formnovalidate") !== undefined) {
                            validator.cancelSubmit = true;
                        }
                    });

                    // validate the form on submit
                    this.submit(function (event) {
                        if (validator.settings.debug) {
                            // prevent form submit to be able to see console output
                            event.preventDefault();
                        }

                        function handle() {
                            var hidden;
                            if (validator.settings.submitHandler) {
                                if (validator.submitButton) {
                                    // insert a hidden input as a replacement for the missing submit button
                                    hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val($(validator.submitButton).val()).appendTo(validator.currentForm);
                                }
                                validator.settings.submitHandler.call(validator, validator.currentForm, event);
                                if (validator.submitButton) {
                                    // and clean up afterwards; thanks to no-block-scope, hidden can be referenced
                                    hidden.remove();
                                }
                                return false;
                            }
                            return true;
                        }

                        // prevent submit for invalid forms or custom submit handlers
                        if (validator.cancelSubmit) {
                            validator.cancelSubmit = false;
                            return handle();
                        }
                        if (validator.form()) {
                            if (validator.pendingRequest) {
                                validator.formSubmitted = true;
                                return false;
                            }
                            return handle();
                        } else {
                            validator.focusInvalid();
                            return false;
                        }
                    });
                }

                return validator;
            },
            // http://docs.jquery.com/Plugins/Validation/valid
            valid: function () {
                if ($(this[0]).is("form")) {
                    return this.validate().form();
                } else {
                    var valid = true;
                    var validator = $(this[0].form).validate();
                    this.each(function () {
                        valid = valid && validator.element(this);
                    });
                    return valid;
                }
            },
            // attributes: space seperated list of attributes to retrieve and remove
            removeAttrs: function (attributes) {
                var result = {},
                    $element = this;
                $.each(attributes.split(/\s/), function (index, value) {
                    result[value] = $element.attr(value);
                    $element.removeAttr(value);
                });
                return result;
            },
            // http://docs.jquery.com/Plugins/Validation/rules
            rules: function (command, argument) {
                var element = this[0];

                if (command) {
                    var settings = $.data(element.form, "validator").settings;
                    var staticRules = settings.rules;
                    var existingRules = $.validator.staticRules(element);
                    switch (command) {
                        case "add":
                            $.extend(existingRules, $.validator.normalizeRule(argument));
                            staticRules[element.name] = existingRules;
                            if (argument.messages) {
                                settings.messages[element.name] = $.extend(settings.messages[element.name], argument.messages);
                            }
                            break;
                        case "remove":
                            if (!argument) {
                                delete staticRules[element.name];
                                return existingRules;
                            }
                            var filtered = {};
                            $.each(argument.split(/\s/), function (index, method) {
                                filtered[method] = existingRules[method];
                                delete existingRules[method];
                            });
                            return filtered;
                    }
                }

                var data = $.validator.normalizeRules(
                    $.extend(
                        {},
                        $.validator.classRules(element),
                        $.validator.attributeRules(element),
                        $.validator.dataRules(element),
                        $.validator.staticRules(element)
                    ), element);

                // make sure required is at front
                if (data.required) {
                    var param = data.required;
                    delete data.required;
                    data = $.extend({required: param}, data);
                }

                return data;
            }
        });

// Custom selectors
        $.extend($.expr[":"], {
            // http://docs.jquery.com/Plugins/Validation/blank
            blank: function (a) {
                return !$.trim("" + $(a).val());
            },
            // http://docs.jquery.com/Plugins/Validation/filled
            filled: function (a) {
                return !!$.trim("" + $(a).val());
            },
            // http://docs.jquery.com/Plugins/Validation/unchecked
            unchecked: function (a) {
                return !$(a).prop("checked");
            }
        });

// constructor for validator
        $.validator = function (options, form) {
            this.settings = $.extend(true, {}, $.validator.defaults, options);
            this.currentForm = form;
            this.init();
        };

        $.validator.format = function (source, params) {
            if (arguments.length === 1) {
                return function () {
                    var args = $.makeArray(arguments);
                    args.unshift(source);
                    return $.validator.format.apply(this, args);
                };
            }
            if (arguments.length > 2 && params.constructor !== Array) {
                params = $.makeArray(arguments).slice(1);
            }
            if (params.constructor !== Array) {
                params = [params];
            }
            $.each(params, function (i, n) {
                source = source.replace(new RegExp("\\{" + i + "\\}", "g"), function () {
                    return n;
                });
            });
            return source;
        };

        $.extend($.validator, {

            defaults: {
                messages: {},
                groups: {},
                rules: {},
                errorClass: "error",
                validClass: "valid",
                errorElement: "label",
                focusInvalid: true,
                errorContainer: $([]),
                errorLabelContainer: $([]),
                onsubmit: true,
                ignore: ":hidden",
                ignoreTitle: false,
                onfocusin: function (element, event) {
                    this.lastActive = element;

                    // hide error label and remove error class on focus if enabled
                    if (this.settings.focusCleanup && !this.blockFocusCleanup) {
                        if (this.settings.unhighlight) {
                            this.settings.unhighlight.call(this, element, this.settings.errorClass, this.settings.validClass);
                        }
                        this.addWrapper(this.errorsFor(element)).hide();
                    }
                },
                onfocusout: function (element, event) {
                    if (!this.checkable(element) && (element.name in this.submitted || !this.optional(element))) {
                        this.element(element);
                    }
                },
                onkeyup: function (element, event) {
                    if (event.which === 9 && this.elementValue(element) === "") {
                        return;
                    } else if (element.name in this.submitted || element === this.lastElement) {
                        this.element(element);
                    }
                },
                onclick: function (element, event) {
                    // click on selects, radiobuttons and checkboxes
                    if (element.name in this.submitted) {
                        this.element(element);
                    }
                    // or option elements, check parent select in that case
                    else if (element.parentNode.name in this.submitted) {
                        this.element(element.parentNode);
                    }
                },
                highlight: function (element, errorClass, validClass) {
                    if (element.type === "radio") {
                        this.findByName(element.name).addClass(errorClass).removeClass(validClass);
                    } else {
                        $(element).addClass(errorClass).removeClass(validClass);
                    }
                },
                unhighlight: function (element, errorClass, validClass) {
                    if (element.type === "radio") {
                        this.findByName(element.name).removeClass(errorClass).addClass(validClass);
                    } else {
                        $(element).removeClass(errorClass).addClass(validClass);
                    }
                }
            },

            // http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
            setDefaults: function (settings) {
                $.extend($.validator.defaults, settings);
            },

            messages: {
                required: "This field is required.",
                remote: "Please fix this field.",
                email: "Please enter a valid email address.",
                url: "Please enter a valid URL.",
                date: "Please enter a valid date.",
                dateISO: "Please enter a valid date (ISO).",
                number: "Please enter a valid number.",
                digits: "Please enter only digits.",
                creditcard: "Please enter a valid credit card number.",
                equalTo: "Please enter the same value again.",
                maxlength: $.validator.format("Please enter no more than {0} characters."),
                minlength: $.validator.format("Please enter at least {0} characters."),
                rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
                range: $.validator.format("Please enter a value between {0} and {1}."),
                max: $.validator.format("Please enter a value less than or equal to {0}."),
                min: $.validator.format("Please enter a value greater than or equal to {0}.")
            },

            autoCreateRanges: false,

            prototype: {

                init: function () {
                    this.labelContainer = $(this.settings.errorLabelContainer);
                    this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
                    this.containers = $(this.settings.errorContainer).add(this.settings.errorLabelContainer);
                    this.submitted = {};
                    this.valueCache = {};
                    this.pendingRequest = 0;
                    this.pending = {};
                    this.invalid = {};
                    this.reset();

                    var groups = (this.groups = {});
                    $.each(this.settings.groups, function (key, value) {
                        if (typeof value === "string") {
                            value = value.split(/\s/);
                        }
                        $.each(value, function (index, name) {
                            groups[name] = key;
                        });
                    });
                    var rules = this.settings.rules;
                    $.each(rules, function (key, value) {
                        rules[key] = $.validator.normalizeRule(value);
                    });

                    function delegate(event) {
                        var validator = $.data(this[0].form, "validator"),
                            eventType = "on" + event.type.replace(/^validate/, "");
                        if (validator.settings[eventType]) {
                            validator.settings[eventType].call(validator, this[0], event);
                        }
                    }

                    $(this.currentForm)
                        .validateDelegate(":text, [type='password'], [type='file'], select, textarea, " +
                            "[type='number'], [type='search'] ,[type='tel'], [type='url'], " +
                            "[type='email'], [type='datetime'], [type='date'], [type='month'], " +
                            "[type='week'], [type='time'], [type='datetime-local'], " +
                            "[type='range'], [type='color'] ",
                            "focusin focusout keyup", delegate)
                        .validateDelegate("[type='radio'], [type='checkbox'], select, option", "click", delegate);

                    if (this.settings.invalidHandler) {
                        $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
                    }
                },

                // http://docs.jquery.com/Plugins/Validation/Validator/form
                form: function () {
                    this.checkForm();
                    $.extend(this.submitted, this.errorMap);
                    this.invalid = $.extend({}, this.errorMap);
                    if (!this.valid()) {
                        $(this.currentForm).triggerHandler("invalid-form", [this]);
                    }
                    this.showErrors();
                    return this.valid();
                },

                checkForm: function () {
                    this.prepareForm();
                    for (var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++) {
                        this.check(elements[i]);
                    }
                    return this.valid();
                },

                // http://docs.jquery.com/Plugins/Validation/Validator/element
                element: function (element) {
                    element = this.validationTargetFor(this.clean(element));
                    this.lastElement = element;
                    this.prepareElement(element);
                    this.currentElements = $(element);
                    var result = this.check(element) !== false;
                    if (result) {
                        delete this.invalid[element.name];
                    } else {
                        this.invalid[element.name] = true;
                    }
                    if (!this.numberOfInvalids()) {
                        // Hide error containers on last error
                        this.toHide = this.toHide.add(this.containers);
                    }
                    this.showErrors();
                    return result;
                },

                // http://docs.jquery.com/Plugins/Validation/Validator/showErrors
                showErrors: function (errors) {
                    if (errors) {
                        // add items to error list and map
                        $.extend(this.errorMap, errors);
                        this.errorList = [];
                        for (var name in errors) {
                            this.errorList.push({
                                message: errors[name],
                                element: this.findByName(name)[0]
                            });
                        }
                        // remove items from success list
                        this.successList = $.grep(this.successList, function (element) {
                            return !(element.name in errors);
                        });
                    }
                    if (this.settings.showErrors) {
                        this.settings.showErrors.call(this, this.errorMap, this.errorList);
                    } else {
                        this.defaultShowErrors();
                    }
                },

                // http://docs.jquery.com/Plugins/Validation/Validator/resetForm
                resetForm: function () {
                    if ($.fn.resetForm) {
                        $(this.currentForm).resetForm();
                    }
                    this.submitted = {};
                    this.lastElement = null;
                    this.prepareForm();
                    this.hideErrors();
                    this.elements().removeClass(this.settings.errorClass).removeData("previousValue");
                },

                numberOfInvalids: function () {
                    return this.objectLength(this.invalid);
                },

                objectLength: function (obj) {
                    var count = 0;
                    for (var i in obj) {
                        count++;
                    }
                    return count;
                },

                hideErrors: function () {
                    this.addWrapper(this.toHide).hide();
                },

                valid: function () {
                    return this.size() === 0;
                },

                size: function () {
                    return this.errorList.length;
                },

                focusInvalid: function () {
                    if (this.settings.focusInvalid) {
                        try {
                            $(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
                                .filter(":visible")
                                .focus()
                                // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
                                .trigger("focusin");
                        } catch (e) {
                            // ignore IE throwing errors when focusing hidden elements
                        }
                    }
                },

                findLastActive: function () {
                    var lastActive = this.lastActive;
                    return lastActive && $.grep(this.errorList, function (n) {
                        return n.element.name === lastActive.name;
                    }).length === 1 && lastActive;
                },

                elements: function () {
                    var validator = this,
                        rulesCache = {};

                    // select all valid inputs inside the form (no submit or reset buttons)
                    return $(this.currentForm)
                        .find("input, select, textarea")
                        .not(":submit, :reset, :image, [disabled]")
                        .not(this.settings.ignore)
                        .filter(function () {
                            if (!this.name && validator.settings.debug && window.console) {
                                console.error("%o has no name assigned", this);
                            }

                            // select only the first element for each name, and only those with rules specified
                            if (this.name in rulesCache || !validator.objectLength($(this).rules())) {
                                return false;
                            }

                            rulesCache[this.name] = true;
                            return true;
                        });
                },

                clean: function (selector) {
                    return $(selector)[0];
                },

                errors: function () {
                    var errorClass = this.settings.errorClass.replace(" ", ".");
                    return $(this.settings.errorElement + "." + errorClass, this.errorContext);
                },

                reset: function () {
                    this.successList = [];
                    this.errorList = [];
                    this.errorMap = {};
                    this.toShow = $([]);
                    this.toHide = $([]);
                    this.currentElements = $([]);
                },

                prepareForm: function () {
                    this.reset();
                    this.toHide = this.errors().add(this.containers);
                },

                prepareElement: function (element) {
                    this.reset();
                    this.toHide = this.errorsFor(element);
                },

                elementValue: function (element) {
                    var type = $(element).attr("type"),
                        val = $(element).val();

                    if (type === "radio" || type === "checkbox") {
                        return $("input[name='" + $(element).attr("name") + "']:checked").val();
                    }

                    if (typeof val === "string") {
                        return val.replace(/\r/g, "");
                    }
                    return val;
                },

                check: function (element) {
                    element = this.validationTargetFor(this.clean(element));

                    var rules = $(element).rules();
                    var dependencyMismatch = false;
                    var val = this.elementValue(element);
                    var result;

                    for (var method in rules) {
                        var rule = {method: method, parameters: rules[method]};
                        try {

                            result = $.validator.methods[method].call(this, val, element, rule.parameters);

                            // if a method indicates that the field is optional and therefore valid,
                            // don't mark it as valid when there are no other rules
                            if (result === "dependency-mismatch") {
                                dependencyMismatch = true;
                                continue;
                            }
                            dependencyMismatch = false;

                            if (result === "pending") {
                                this.toHide = this.toHide.not(this.errorsFor(element));
                                return;
                            }

                            if (!result) {
                                this.formatAndAdd(element, rule);
                                return false;
                            }
                        } catch (e) {
                            if (this.settings.debug && window.console) {
                                console.log("Exception occured when checking element " + element.id + ", check the '" + rule.method + "' method.", e);
                            }
                            throw e;
                        }
                    }
                    if (dependencyMismatch) {
                        return;
                    }
                    if (this.objectLength(rules)) {
                        this.successList.push(element);
                    }
                    return true;
                },

                // return the custom message for the given element and validation method
                // specified in the element's HTML5 data attribute
                customDataMessage: function (element, method) {
                    return $(element).data("msg-" + method.toLowerCase()) || (element.attributes && $(element).attr("data-msg-" + method.toLowerCase()));
                },

                // return the custom message for the given element name and validation method
                customMessage: function (name, method) {
                    var m = this.settings.messages[name];
                    return m && (m.constructor === String ? m : m[method]);
                },

                // return the first defined argument, allowing empty strings
                findDefined: function () {
                    for (var i = 0; i < arguments.length; i++) {
                        if (arguments[i] !== undefined) {
                            return arguments[i];
                        }
                    }
                    return undefined;
                },

                defaultMessage: function (element, method) {
                    return this.findDefined(
                        this.customMessage(element.name, method),
                        this.customDataMessage(element, method),
                        // title is never undefined, so handle empty string as undefined
                        !this.settings.ignoreTitle && element.title || undefined,
                        $.validator.messages[method],
                        "<strong>Warning: No message defined for " + element.name + "</strong>"
                    );
                },

                formatAndAdd: function (element, rule) {
                    var message = this.defaultMessage(element, rule.method),
                        theregex = /\$?\{(\d+)\}/g;
                    if (typeof message === "function") {
                        message = message.call(this, rule.parameters, element);
                    } else if (theregex.test(message)) {
                        message = $.validator.format(message.replace(theregex, "{$1}"), rule.parameters);
                    }
                    this.errorList.push({
                        message: message,
                        element: element
                    });

                    this.errorMap[element.name] = message;
                    this.submitted[element.name] = message;
                },

                addWrapper: function (toToggle) {
                    if (this.settings.wrapper) {
                        toToggle = toToggle.add(toToggle.parent(this.settings.wrapper));
                    }
                    return toToggle;
                },

                defaultShowErrors: function () {
                    var i, elements;
                    for (i = 0; this.errorList[i]; i++) {
                        var error = this.errorList[i];
                        if (this.settings.highlight) {
                            this.settings.highlight.call(this, error.element, this.settings.errorClass, this.settings.validClass);
                        }
                        this.showLabel(error.element, error.message);
                    }
                    if (this.errorList.length) {
                        this.toShow = this.toShow.add(this.containers);
                    }
                    if (this.settings.success) {
                        for (i = 0; this.successList[i]; i++) {
                            this.showLabel(this.successList[i]);
                        }
                    }
                    if (this.settings.unhighlight) {
                        for (i = 0, elements = this.validElements(); elements[i]; i++) {
                            this.settings.unhighlight.call(this, elements[i], this.settings.errorClass, this.settings.validClass);
                        }
                    }
                    this.toHide = this.toHide.not(this.toShow);
                    this.hideErrors();
                    this.addWrapper(this.toShow).show();
                },

                validElements: function () {
                    return this.currentElements.not(this.invalidElements());
                },

                invalidElements: function () {
                    return $(this.errorList).map(function () {
                        return this.element;
                    });
                },

                showLabel: function (element, message) {
                    var label = this.errorsFor(element);
                    if (label.length) {
                        // refresh error/success class
                        label.removeClass(this.settings.validClass).addClass(this.settings.errorClass);
                        // replace message on existing label
                        label.html(message);
                    } else {
                        // create label
                        label = $("<" + this.settings.errorElement + ">")
                            .attr("for", this.idOrName(element))
                            .addClass(this.settings.errorClass)
                            .html(message || "");
                        if (this.settings.wrapper) {
                            // make sure the element is visible, even in IE
                            // actually showing the wrapped element is handled elsewhere
                            label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
                        }
                        if (!this.labelContainer.append(label).length) {
                            if (this.settings.errorPlacement) {
                                this.settings.errorPlacement(label, $(element));
                            } else {
                                label.insertAfter(element);
                            }
                        }
                    }
                    if (!message && this.settings.success) {
                        label.text("");
                        if (typeof this.settings.success === "string") {
                            label.addClass(this.settings.success);
                        } else {
                            this.settings.success(label, element);
                        }
                    }
                    this.toShow = this.toShow.add(label);
                },

                errorsFor: function (element) {
                    var name = this.idOrName(element);
                    return this.errors().filter(function () {
                        return $(this).attr("for") === name;
                    });
                },

                idOrName: function (element) {
                    return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
                },

                validationTargetFor: function (element) {
                    // if radio/checkbox, validate first element in group instead
                    if (this.checkable(element)) {
                        element = this.findByName(element.name).not(this.settings.ignore)[0];
                    }
                    return element;
                },

                checkable: function (element) {
                    return (/radio|checkbox/i).test(element.type);
                },

                findByName: function (name) {
                    return $(this.currentForm).find("[name='" + name + "']");
                },

                getLength: function (value, element) {
                    switch (element.nodeName.toLowerCase()) {
                        case "select":
                            return $("option:selected", element).length;
                        case "input":
                            if (this.checkable(element)) {
                                return this.findByName(element.name).filter(":checked").length;
                            }
                    }
                    return value.length;
                },

                depend: function (param, element) {
                    return this.dependTypes[typeof param] ? this.dependTypes[typeof param](param, element) : true;
                },

                dependTypes: {
                    "boolean": function (param, element) {
                        return param;
                    },
                    "string": function (param, element) {
                        return !!$(param, element.form).length;
                    },
                    "function": function (param, element) {
                        return param(element);
                    }
                },

                optional: function (element) {
                    var val = this.elementValue(element);
                    return !$.validator.methods.required.call(this, val, element) && "dependency-mismatch";
                },

                startRequest: function (element) {
                    if (!this.pending[element.name]) {
                        this.pendingRequest++;
                        this.pending[element.name] = true;
                    }
                },

                stopRequest: function (element, valid) {
                    this.pendingRequest--;
                    // sometimes synchronization fails, make sure pendingRequest is never < 0
                    if (this.pendingRequest < 0) {
                        this.pendingRequest = 0;
                    }
                    delete this.pending[element.name];
                    if (valid && this.pendingRequest === 0 && this.formSubmitted && this.form()) {
                        $(this.currentForm).submit();
                        this.formSubmitted = false;
                    } else if (!valid && this.pendingRequest === 0 && this.formSubmitted) {
                        $(this.currentForm).triggerHandler("invalid-form", [this]);
                        this.formSubmitted = false;
                    }
                },

                previousValue: function (element) {
                    return $.data(element, "previousValue") || $.data(element, "previousValue", {
                        old: null,
                        valid: true,
                        message: this.defaultMessage(element, "remote")
                    });
                }

            },

            classRuleSettings: {
                required: {required: true},
                email: {email: true},
                url: {url: true},
                date: {date: true},
                dateISO: {dateISO: true},
                number: {number: true},
                digits: {digits: true},
                creditcard: {creditcard: true}
            },

            addClassRules: function (className, rules) {
                if (className.constructor === String) {
                    this.classRuleSettings[className] = rules;
                } else {
                    $.extend(this.classRuleSettings, className);
                }
            },

            classRules: function (element) {
                var rules = {};
                var classes = $(element).attr("class");
                if (classes) {
                    $.each(classes.split(" "), function () {
                        if (this in $.validator.classRuleSettings) {
                            $.extend(rules, $.validator.classRuleSettings[this]);
                        }
                    });
                }
                return rules;
            },

            attributeRules: function (element) {
                var rules = {};
                var $element = $(element);

                for (var method in $.validator.methods) {
                    var value;

                    // support for <input required> in both html5 and older browsers
                    if (method === "required") {
                        value = $element.get(0).getAttribute(method);
                        // Some browsers return an empty string for the required attribute
                        // and non-HTML5 browsers might have required="" markup
                        if (value === "") {
                            value = true;
                        }
                        // force non-HTML5 browsers to return bool
                        value = !!value;
                    } else {
                        value = $element.attr(method);
                    }

                    if (value) {
                        rules[method] = value;
                    } else if ($element[0].getAttribute("type") === method) {
                        rules[method] = true;
                    }
                }

                // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
                if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
                    delete rules.maxlength;
                }

                return rules;
            },

            dataRules: function (element) {
                var method, value,
                    rules = {}, $element = $(element);
                for (method in $.validator.methods) {
                    value = $element.data("rule-" + method.toLowerCase());
                    if (value !== undefined) {
                        rules[method] = value;
                    }
                }
                return rules;
            },

            staticRules: function (element) {
                var rules = {};
                var validator = $.data(element.form, "validator");
                if (validator.settings.rules) {
                    rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
                }
                return rules;
            },

            normalizeRules: function (rules, element) {
                // handle dependency check
                $.each(rules, function (prop, val) {
                    // ignore rule when param is explicitly false, eg. required:false
                    if (val === false) {
                        delete rules[prop];
                        return;
                    }
                    if (val.param || val.depends) {
                        var keepRule = true;
                        switch (typeof val.depends) {
                            case "string":
                                keepRule = !!$(val.depends, element.form).length;
                                break;
                            case "function":
                                keepRule = val.depends.call(element, element);
                                break;
                        }
                        if (keepRule) {
                            rules[prop] = val.param !== undefined ? val.param : true;
                        } else {
                            delete rules[prop];
                        }
                    }
                });

                // evaluate parameters
                $.each(rules, function (rule, parameter) {
                    rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
                });

                // clean number parameters
                $.each(['minlength', 'maxlength'], function () {
                    if (rules[this]) {
                        rules[this] = Number(rules[this]);
                    }
                });
                $.each(['rangelength'], function () {
                    var parts;
                    if (rules[this]) {
                        if ($.isArray(rules[this])) {
                            rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
                        } else if (typeof rules[this] === "string") {
                            parts = rules[this].split(/[\s,]+/);
                            rules[this] = [Number(parts[0]), Number(parts[1])];
                        }
                    }
                });

                if ($.validator.autoCreateRanges) {
                    // auto-create ranges
                    if (rules.min && rules.max) {
                        rules.range = [rules.min, rules.max];
                        delete rules.min;
                        delete rules.max;
                    }
                    if (rules.minlength && rules.maxlength) {
                        rules.rangelength = [rules.minlength, rules.maxlength];
                        delete rules.minlength;
                        delete rules.maxlength;
                    }
                }

                return rules;
            },

            // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
            normalizeRule: function (data) {
                if (typeof data === "string") {
                    var transformed = {};
                    $.each(data.split(/\s/), function () {
                        transformed[this] = true;
                    });
                    data = transformed;
                }
                return data;
            },

            // http://docs.jquery.com/Plugins/Validation/Validator/addMethod
            addMethod: function (name, method, message) {
                $.validator.methods[name] = method;
                $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];
                if (method.length < 3) {
                    $.validator.addClassRules(name, $.validator.normalizeRule(name));
                }
            },

            methods: {

                // http://docs.jquery.com/Plugins/Validation/Methods/required
                required: function (value, element, param) {
                    // check if dependency is met
                    if (!this.depend(param, element)) {
                        return "dependency-mismatch";
                    }
                    if (element.nodeName.toLowerCase() === "select") {
                        // could be an array for select-multiple or a string, both are fine this way
                        var val = $(element).val();
                        return val && val.length > 0;
                    }
                    if (this.checkable(element)) {
                        return this.getLength(value, element) > 0;
                    }
                    return $.trim(value).length > 0;
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/remote
                remote: function (value, element, param) {
                    if (this.optional(element)) {
                        return "dependency-mismatch";
                    }

                    var previous = this.previousValue(element);
                    if (!this.settings.messages[element.name]) {
                        this.settings.messages[element.name] = {};
                    }
                    previous.originalMessage = this.settings.messages[element.name].remote;
                    this.settings.messages[element.name].remote = previous.message;

                    param = typeof param === "string" && {url: param} || param;

                    if (previous.old === value) {
                        return previous.valid;
                    }

                    previous.old = value;
                    var validator = this;
                    this.startRequest(element);
                    var data = {};
                    data[element.name] = value;
                    $.ajax($.extend(true, {
                        url: param,
                        mode: "abort",
                        port: "validate" + element.name,
                        dataType: "json",
                        data: data,
                        success: function (response) {
                            validator.settings.messages[element.name].remote = previous.originalMessage;
                            var valid = response === true || response === "true";
                            if (valid) {
                                var submitted = validator.formSubmitted;
                                validator.prepareElement(element);
                                validator.formSubmitted = submitted;
                                validator.successList.push(element);
                                delete validator.invalid[element.name];
                                validator.showErrors();
                            } else {
                                var errors = {};
                                var message = response || validator.defaultMessage(element, "remote");
                                errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
                                validator.invalid[element.name] = true;
                                validator.showErrors(errors);
                            }
                            previous.valid = valid;
                            validator.stopRequest(element, valid);
                        }
                    }, param));
                    return "pending";
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/email
                email: function (value, element) {
                    // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
                    return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(value);
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/url
                url: function (value, element) {
                    // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
                    return this.optional(element) || /^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/date
                date: function (value, element) {
                    return this.optional(element) || !/Invalid|NaN/.test(new Date(value).toString());
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/dateISO
                dateISO: function (value, element) {
                    return this.optional(element) || /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(value);
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/number
                number: function (value, element) {
                    return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/digits
                digits: function (value, element) {
                    return this.optional(element) || /^\d+$/.test(value);
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/creditcard
                // based on http://en.wikipedia.org/wiki/Luhn
                creditcard: function (value, element) {
                    if (this.optional(element)) {
                        return "dependency-mismatch";
                    }
                    // accept only spaces, digits and dashes
                    if (/[^0-9 \-]+/.test(value)) {
                        return false;
                    }
                    var nCheck = 0,
                        nDigit = 0,
                        bEven = false;

                    value = value.replace(/\D/g, "");

                    for (var n = value.length - 1; n >= 0; n--) {
                        var cDigit = value.charAt(n);
                        nDigit = parseInt(cDigit, 10);
                        if (bEven) {
                            if ((nDigit *= 2) > 9) {
                                nDigit -= 9;
                            }
                        }
                        nCheck += nDigit;
                        bEven = !bEven;
                    }

                    return (nCheck % 10) === 0;
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/minlength
                minlength: function (value, element, param) {
                    var length = $.isArray(value) ? value.length : this.getLength($.trim(value), element);
                    return this.optional(element) || length >= param;
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/maxlength
                maxlength: function (value, element, param) {
                    var length = $.isArray(value) ? value.length : this.getLength($.trim(value), element);
                    return this.optional(element) || length <= param;
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/rangelength
                rangelength: function (value, element, param) {
                    var length = $.isArray(value) ? value.length : this.getLength($.trim(value), element);
                    return this.optional(element) || (length >= param[0] && length <= param[1]);
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/min
                min: function (value, element, param) {
                    return this.optional(element) || value >= param;
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/max
                max: function (value, element, param) {
                    return this.optional(element) || value <= param;
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/range
                range: function (value, element, param) {
                    return this.optional(element) || (value >= param[0] && value <= param[1]);
                },

                // http://docs.jquery.com/Plugins/Validation/Methods/equalTo
                equalTo: function (value, element, param) {
                    // bind to the blur event of the target in order to revalidate whenever the target field is updated
                    // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
                    var target = $(param);
                    if (this.settings.onfocusout) {
                        target.unbind(".validate-equalTo").bind("blur.validate-equalTo", function () {
                            $(element).valid();
                        });
                    }
                    return value === target.val();
                }

            }

        });

// deprecated, use $.validator.format instead
        $.format = $.validator.format;

    }(jQuery));

    // ajax mode: abort
    // usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
    // if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
    (function ($) {
        var pendingRequests = {};
        // Use a prefilter if available (1.5+)
        if ($.ajaxPrefilter) {
            $.ajaxPrefilter(function (settings, _, xhr) {
                var port = settings.port;
                if (settings.mode === "abort") {
                    if (pendingRequests[port]) {
                        pendingRequests[port].abort();
                    }
                    pendingRequests[port] = xhr;
                }
            });
        } else {
            // Proxy ajax
            var ajax = $.ajax;
            $.ajax = function (settings) {
                var mode = ("mode" in settings ? settings : $.ajaxSettings).mode,
                    port = ("port" in settings ? settings : $.ajaxSettings).port;
                if (mode === "abort") {
                    if (pendingRequests[port]) {
                        pendingRequests[port].abort();
                    }
                    pendingRequests[port] = ajax.apply(this, arguments);
                    return pendingRequests[port];
                }
                return ajax.apply(this, arguments);
            };
        }
    }(jQuery));

    // provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
    // handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
    (function ($) {
        $.extend($.fn, {
            validateDelegate: function (delegate, type, handler) {
                return this.bind(type, function (event) {
                    var target = $(event.target);
                    if (target.is(delegate)) {
                        return handler.apply(target, arguments);
                    }
                });
            }
        });
    }(jQuery));

    /*!
 * jQuery Form Plugin
 * version: 3.51.0-2014.06.20
 * Requires jQuery v1.5 or later
 * Copyright (c) 2014 M. Alsup
 * Examples and documentation at: http://malsup.com/jquery/form/
 * Project repository: https://github.com/malsup/form
 * Dual licensed under the MIT and GPL licenses.
 * https://github.com/malsup/form#copyright-and-license
 */
    /*global ActiveXObject */

    // AMD support
    (function (factory) {
        "use strict";
        if (typeof define === 'function' && define.amd) {
            // using AMD; register as anon module
            define(['jquery'], factory);
        } else {
            // no AMD; invoke directly
            factory((typeof (jQuery) != 'undefined') ? jQuery : window.Zepto);
        }
    }

    (function ($) {
        "use strict";

        /*
          Usage Note:
          -----------
          Do not use both ajaxSubmit and ajaxForm on the same form.  These
          functions are mutually exclusive.  Use ajaxSubmit if you want
          to bind your own submit handler to the form.  For example,

          $(document).ready(function() {
              $('#myForm').on('submit', function(e) {
                  e.preventDefault(); // <-- important
                  $(this).ajaxSubmit({
                      target: '#output'
                  });
              });
          });

          Use ajaxForm when you want the plugin to manage all the event binding
          for you.  For example,

          $(document).ready(function() {
              $('#myForm').ajaxForm({
                  target: '#output'
              });
          });

          You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
          form does not have to exist when you invoke ajaxForm:

          $('#myForm').ajaxForm({
              delegation: true,
              target: '#output'
          });

          When using ajaxForm, the ajaxSubmit function will be invoked for you
          at the appropriate time.
      */

        /**
         * Feature detection
         */
        var feature = {};
        feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
        feature.formdata = window.FormData !== undefined;

        var hasProp = !!$.fn.prop;

// attr2 uses prop when it can but checks the return type for
// an expected string.  this accounts for the case where a form
// contains inputs with names like "action" or "method"; in those
// cases "prop" returns the element
        $.fn.attr2 = function () {
            if (!hasProp) {
                return this.attr.apply(this, arguments);
            }
            var val = this.prop.apply(this, arguments);
            if ((val && val.jquery) || typeof val === 'string') {
                return val;
            }
            return this.attr.apply(this, arguments);
        };

        /**
         * ajaxSubmit() provides a mechanism for immediately submitting
         * an HTML form using AJAX.
         */
        $.fn.ajaxSubmit = function (options) {
            /*jshint scripturl:true */

            // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
            if (!this.length) {
                log('ajaxSubmit: skipping submit process - no element selected');
                return this;
            }

            var method, action, url, $form = this;

            if (typeof options == 'function') {
                options = {success: options};
            } else if (options === undefined) {
                options = {};
            }

            method = options.type || this.attr2('method');
            action = options.url || this.attr2('action');

            url = (typeof action === 'string') ? $.trim(action) : '';
            url = url || window.location.href || '';
            if (url) {
                // clean url (don't include hash vaue)
                url = (url.match(/^([^#]+)/) || [])[1];
            }

            options = $.extend(true, {
                url: url,
                success: $.ajaxSettings.success,
                type: method || $.ajaxSettings.type,
                iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
            }, options);

            // hook for manipulating the form data before it is extracted;
            // convenient for use with rich editors like tinyMCE or FCKEditor
            var veto = {};
            this.trigger('form-pre-serialize', [this, options, veto]);
            if (veto.veto) {
                log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
                return this;
            }

            // provide opportunity to alter form data before it is serialized
            if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
                log('ajaxSubmit: submit aborted via beforeSerialize callback');
                return this;
            }

            var traditional = options.traditional;
            if (traditional === undefined) {
                traditional = $.ajaxSettings.traditional;
            }

            var elements = [];
            var qx, a = this.formToArray(options.semantic, elements);
            if (options.data) {
                options.extraData = options.data;
                qx = $.param(options.data, traditional);
            }

            // give pre-submit callback an opportunity to abort the submit
            if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
                log('ajaxSubmit: submit aborted via beforeSubmit callback');
                return this;
            }

            // fire vetoable 'validate' event
            this.trigger('form-submit-validate', [a, this, options, veto]);
            if (veto.veto) {
                log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
                return this;
            }

            var q = $.param(a, traditional);
            if (qx) {
                q = (q ? (q + '&' + qx) : qx);
            }
            if (options.type.toUpperCase() == 'GET') {
                options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
                options.data = null;  // data is null for 'get'
            } else {
                options.data = q; // data is the query string for 'post'
            }

            var callbacks = [];
            if (options.resetForm) {
                callbacks.push(function () {
                    $form.resetForm();
                });
            }
            if (options.clearForm) {
                callbacks.push(function () {
                    $form.clearForm(options.includeHidden);
                });
            }

            // perform a load on the target only if dataType is not provided
            if (!options.dataType && options.target) {
                var oldSuccess = options.success || function () {
                };
                callbacks.push(function (data) {
                    var fn = options.replaceTarget ? 'replaceWith' : 'html';
                    $(options.target)[fn](data).each(oldSuccess, arguments);
                });
            } else if (options.success) {
                callbacks.push(options.success);
            }

            options.success = function (data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
                var context = options.context || this;    // jQuery 1.4+ supports scope context
                for (var i = 0, max = callbacks.length; i < max; i++) {
                    callbacks[i].apply(context, [data, status, xhr || $form, $form]);
                }
            };

            if (options.error) {
                var oldError = options.error;
                options.error = function (xhr, status, error) {
                    var context = options.context || this;
                    oldError.apply(context, [xhr, status, error, $form]);
                };
            }

            if (options.complete) {
                var oldComplete = options.complete;
                options.complete = function (xhr, status) {
                    var context = options.context || this;
                    oldComplete.apply(context, [xhr, status, $form]);
                };
            }

            // are there files to upload?

            // [value] (issue #113), also see comment:
            // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
            var fileInputs = $('input[type=file]:enabled', this).filter(function () {
                return $(this).val() !== '';
            });

            var hasFileInputs = fileInputs.length > 0;
            var mp = 'multipart/form-data';
            var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);

            var fileAPI = feature.fileapi && feature.formdata;
            log("fileAPI :" + fileAPI);
            var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;

            var jqxhr;

            // options.iframe allows user to force iframe mode
            // 06-NOV-09: now defaulting to iframe mode if file input is detected
            if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
                // hack to fix Safari hang (thanks to Tim Molendijk for this)
                // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
                if (options.closeKeepAlive) {
                    $.get(options.closeKeepAlive, function () {
                        jqxhr = fileUploadIframe(a);
                    });
                } else {
                    jqxhr = fileUploadIframe(a);
                }
            } else if ((hasFileInputs || multipart) && fileAPI) {
                jqxhr = fileUploadXhr(a);
            } else {
                jqxhr = $.ajax(options);
            }

            $form.removeData('jqxhr').data('jqxhr', jqxhr);

            // clear element array
            for (var k = 0; k < elements.length; k++) {
                elements[k] = null;
            }

            // fire 'notify' event
            this.trigger('form-submit-notify', [this, options]);
            return this;

            // utility fn for deep serialization
            function deepSerialize(extraData) {
                var serialized = $.param(extraData, options.traditional).split('&');
                var len = serialized.length;
                var result = [];
                var i, part;
                for (i = 0; i < len; i++) {
                    // #252; undo param space replacement
                    serialized[i] = serialized[i].replace(/\+/g, ' ');
                    part = serialized[i].split('=');
                    // #278; use array instead of object storage, favoring array serializations
                    result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
                }
                return result;
            }

            // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
            function fileUploadXhr(a) {
                var formdata = new FormData();

                for (var i = 0; i < a.length; i++) {
                    formdata.append(a[i].name, a[i].value);
                }

                if (options.extraData) {
                    var serializedData = deepSerialize(options.extraData);
                    for (i = 0; i < serializedData.length; i++) {
                        if (serializedData[i]) {
                            formdata.append(serializedData[i][0], serializedData[i][1]);
                        }
                    }
                }

                options.data = null;

                var s = $.extend(true, {}, $.ajaxSettings, options, {
                    contentType: false,
                    processData: false,
                    cache: false,
                    type: method || 'POST'
                });

                if (options.uploadProgress) {
                    // workaround because jqXHR does not expose upload property
                    s.xhr = function () {
                        var xhr = $.ajaxSettings.xhr();
                        if (xhr.upload) {
                            xhr.upload.addEventListener('progress', function (event) {
                                var percent = 0;
                                var position = event.loaded || event.position; /*event.position is deprecated*/
                                var total = event.total;
                                if (event.lengthComputable) {
                                    percent = Math.ceil(position / total * 100);
                                }
                                options.uploadProgress(event, position, total, percent);
                            }, false);
                        }
                        return xhr;
                    };
                }

                s.data = null;
                var beforeSend = s.beforeSend;
                s.beforeSend = function (xhr, o) {
                    //Send FormData() provided by user
                    if (options.formData) {
                        o.data = options.formData;
                    } else {
                        o.data = formdata;
                    }
                    if (beforeSend) {
                        beforeSend.call(this, xhr, o);
                    }
                };
                return $.ajax(s);
            }

            // private function for handling file uploads (hat tip to YAHOO!)
            function fileUploadIframe(a) {
                var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
                var deferred = $.Deferred();

                // #341
                deferred.abort = function (status) {
                    xhr.abort(status);
                };

                if (a) {
                    // ensure that every serialized input is still enabled
                    for (i = 0; i < elements.length; i++) {
                        el = $(elements[i]);
                        if (hasProp) {
                            el.prop('disabled', false);
                        } else {
                            el.removeAttr('disabled');
                        }
                    }
                }

                s = $.extend(true, {}, $.ajaxSettings, options);
                s.context = s.context || s;
                id = 'jqFormIO' + (new Date().getTime());
                if (s.iframeTarget) {
                    $io = $(s.iframeTarget);
                    n = $io.attr2('name');
                    if (!n) {
                        $io.attr2('name', id);
                    } else {
                        id = n;
                    }
                } else {
                    $io = $('<iframe name="' + id + '" src="' + s.iframeSrc + '" />');
                    $io.css({position: 'absolute', top: '-1000px', left: '-1000px'});
                }
                io = $io[0];


                xhr = { // mock object
                    aborted: 0,
                    responseText: null,
                    responseXML: null,
                    status: 0,
                    statusText: 'n/a',
                    getAllResponseHeaders: function () {
                    },
                    getResponseHeader: function () {
                    },
                    setRequestHeader: function () {
                    },
                    abort: function (status) {
                        var e = (status === 'timeout' ? 'timeout' : 'aborted');
                        log('aborting upload... ' + e);
                        this.aborted = 1;

                        try { // #214, #257
                            if (io.contentWindow.document.execCommand) {
                                io.contentWindow.document.execCommand('Stop');
                            }
                        } catch (ignore) {
                        }

                        $io.attr('src', s.iframeSrc); // abort op in progress
                        xhr.error = e;
                        if (s.error) {
                            s.error.call(s.context, xhr, e, status);
                        }
                        if (g) {
                            $.event.trigger("ajaxError", [xhr, s, e]);
                        }
                        if (s.complete) {
                            s.complete.call(s.context, xhr, e);
                        }
                    }
                };

                g = s.global;
                // trigger ajax global events so that activity/block indicators work like normal
                if (g && 0 === $.active++) {
                    $.event.trigger("ajaxStart");
                }
                if (g) {
                    $.event.trigger("ajaxSend", [xhr, s]);
                }

                if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
                    if (s.global) {
                        $.active--;
                    }
                    deferred.reject();
                    return deferred;
                }
                if (xhr.aborted) {
                    deferred.reject();
                    return deferred;
                }

                // add submitting element to data if we know it
                sub = form.clk;
                if (sub) {
                    n = sub.name;
                    if (n && !sub.disabled) {
                        s.extraData = s.extraData || {};
                        s.extraData[n] = sub.value;
                        if (sub.type == "image") {
                            s.extraData[n + '.x'] = form.clk_x;
                            s.extraData[n + '.y'] = form.clk_y;
                        }
                    }
                }

                var CLIENT_TIMEOUT_ABORT = 1;
                var SERVER_ABORT = 2;

                function getDoc(frame) {
                    /* it looks like contentWindow or contentDocument do not
             * carry the protocol property in ie8, when running under ssl
             * frame.document is the only valid response document, since
             * the protocol is know but not on the other two objects. strange?
             * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
             */

                    var doc = null;

                    // IE8 cascading access check
                    try {
                        if (frame.contentWindow) {
                            doc = frame.contentWindow.document;
                        }
                    } catch (err) {
                        // IE8 access denied under ssl & missing protocol
                        log('cannot get iframe.contentWindow document: ' + err);
                    }

                    if (doc) { // successful getting content
                        return doc;
                    }

                    try { // simply checking may throw in ie8 under ssl or mismatched protocol
                        doc = frame.contentDocument ? frame.contentDocument : frame.document;
                    } catch (err) {
                        // last attempt
                        log('cannot get iframe.contentDocument: ' + err);
                        doc = frame.document;
                    }
                    return doc;
                }

                // Rails CSRF hack (thanks to Yvan Barthelemy)
                var csrf_token = $('meta[name=csrf-token]').attr('content');
                var csrf_param = $('meta[name=csrf-param]').attr('content');
                if (csrf_param && csrf_token) {
                    s.extraData = s.extraData || {};
                    s.extraData[csrf_param] = csrf_token;
                }

                // take a breath so that pending repaints get some cpu time before the upload starts
                function doSubmit() {
                    // make sure form attrs are set
                    var t = $form.attr2('target'),
                        a = $form.attr2('action'),
                        mp = 'multipart/form-data',
                        et = $form.attr('enctype') || $form.attr('encoding') || mp;

                    // update form attrs in IE friendly way
                    form.setAttribute('target', id);
                    if (!method || /post/i.test(method)) {
                        form.setAttribute('method', 'POST');
                    }
                    if (a != s.url) {
                        form.setAttribute('action', s.url);
                    }

                    // ie borks in some cases when setting encoding
                    if (!s.skipEncodingOverride && (!method || /post/i.test(method))) {
                        $form.attr({
                            encoding: 'multipart/form-data',
                            enctype: 'multipart/form-data'
                        });
                    }

                    // support timout
                    if (s.timeout) {
                        timeoutHandle = setTimeout(function () {
                            timedOut = true;
                            cb(CLIENT_TIMEOUT_ABORT);
                        }, s.timeout);
                    }

                    // look for server aborts
                    function checkState() {
                        try {
                            var state = getDoc(io).readyState;
                            log('state = ' + state);
                            if (state && state.toLowerCase() == 'uninitialized') {
                                setTimeout(checkState, 50);
                            }
                        } catch (e) {
                            log('Server abort: ', e, ' (', e.name, ')');
                            cb(SERVER_ABORT);
                            if (timeoutHandle) {
                                clearTimeout(timeoutHandle);
                            }
                            timeoutHandle = undefined;
                        }
                    }

                    // add "extra" data to form if provided in options
                    var extraInputs = [];
                    try {
                        if (s.extraData) {
                            for (var n in s.extraData) {
                                if (s.extraData.hasOwnProperty(n)) {
                                    // if using the $.param format that allows for multiple values with the same name
                                    if ($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
                                        extraInputs.push(
                                            $('<input type="hidden" name="' + s.extraData[n].name + '">').val(s.extraData[n].value)
                                                .appendTo(form)[0]);
                                    } else {
                                        extraInputs.push(
                                            $('<input type="hidden" name="' + n + '">').val(s.extraData[n])
                                                .appendTo(form)[0]);
                                    }
                                }
                            }
                        }

                        if (!s.iframeTarget) {
                            // add iframe to doc and submit the form
                            $io.appendTo('body');
                        }
                        if (io.attachEvent) {
                            io.attachEvent('onload', cb);
                        } else {
                            io.addEventListener('load', cb, false);
                        }
                        setTimeout(checkState, 15);

                        try {
                            form.submit();
                        } catch (err) {
                            // just in case form has element with name/id of 'submit'
                            var submitFn = document.createElement('form').submit;
                            submitFn.apply(form);
                        }
                    } finally {
                        // reset attrs and remove "extra" input elements
                        form.setAttribute('action', a);
                        form.setAttribute('enctype', et); // #380
                        if (t) {
                            form.setAttribute('target', t);
                        } else {
                            $form.removeAttr('target');
                        }
                        $(extraInputs).remove();
                    }
                }

                if (s.forceSync) {
                    doSubmit();
                } else {
                    setTimeout(doSubmit, 10); // this lets dom updates render
                }

                var data, doc, domCheckCount = 50, callbackProcessed;

                function cb(e) {
                    if (xhr.aborted || callbackProcessed) {
                        return;
                    }

                    doc = getDoc(io);
                    if (!doc) {
                        log('cannot access response document');
                        e = SERVER_ABORT;
                    }
                    if (e === CLIENT_TIMEOUT_ABORT && xhr) {
                        xhr.abort('timeout');
                        deferred.reject(xhr, 'timeout');
                        return;
                    } else if (e == SERVER_ABORT && xhr) {
                        xhr.abort('server abort');
                        deferred.reject(xhr, 'error', 'server abort');
                        return;
                    }

                    if (!doc || doc.location.href == s.iframeSrc) {
                        // response not received yet
                        if (!timedOut) {
                            return;
                        }
                    }
                    if (io.detachEvent) {
                        io.detachEvent('onload', cb);
                    } else {
                        io.removeEventListener('load', cb, false);
                    }

                    var status = 'success', errMsg;
                    try {
                        if (timedOut) {
                            throw 'timeout';
                        }

                        var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
                        log('isXml=' + isXml);
                        if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
                            if (--domCheckCount) {
                                // in some browsers (Opera) the iframe DOM is not always traversable when
                                // the onload callback fires, so we loop a bit to accommodate
                                log('requeing onLoad callback, DOM not available');
                                setTimeout(cb, 250);
                                return;
                            }
                            // let this fall through because server response could be an empty document
                            //log('Could not access iframe DOM after mutiple tries.');
                            //throw 'DOMException: not available';
                        }

                        //log('response detected');
                        var docRoot = doc.body ? doc.body : doc.documentElement;
                        xhr.responseText = docRoot ? docRoot.innerHTML : null;
                        xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
                        if (isXml) {
                            s.dataType = 'xml';
                        }
                        xhr.getResponseHeader = function (header) {
                            var headers = {'content-type': s.dataType};
                            return headers[header.toLowerCase()];
                        };
                        // support for XHR 'status' & 'statusText' emulation :
                        if (docRoot) {
                            xhr.status = Number(docRoot.getAttribute('status')) || xhr.status;
                            xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
                        }

                        var dt = (s.dataType || '').toLowerCase();
                        var scr = /(json|script|text)/.test(dt);
                        if (scr || s.textarea) {
                            // see if user embedded response in textarea
                            var ta = doc.getElementsByTagName('textarea')[0];
                            if (ta) {
                                xhr.responseText = ta.value;
                                // support for XHR 'status' & 'statusText' emulation :
                                xhr.status = Number(ta.getAttribute('status')) || xhr.status;
                                xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
                            } else if (scr) {
                                // account for browsers injecting pre around json response
                                var pre = doc.getElementsByTagName('pre')[0];
                                var b = doc.getElementsByTagName('body')[0];
                                if (pre) {
                                    xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
                                } else if (b) {
                                    xhr.responseText = b.textContent ? b.textContent : b.innerText;
                                }
                            }
                        } else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
                            xhr.responseXML = toXml(xhr.responseText);
                        }

                        try {
                            data = httpData(xhr, dt, s);
                        } catch (err) {
                            status = 'parsererror';
                            xhr.error = errMsg = (err || status);
                        }
                    } catch (err) {
                        log('error caught: ', err);
                        status = 'error';
                        xhr.error = errMsg = (err || status);
                    }

                    if (xhr.aborted) {
                        log('upload aborted');
                        status = null;
                    }

                    if (xhr.status) { // we've set xhr.status
                        status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
                    }

                    // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
                    if (status === 'success') {
                        if (s.success) {
                            s.success.call(s.context, data, 'success', xhr);
                        }
                        deferred.resolve(xhr.responseText, 'success', xhr);
                        if (g) {
                            $.event.trigger("ajaxSuccess", [xhr, s]);
                        }
                    } else if (status) {
                        if (errMsg === undefined) {
                            errMsg = xhr.statusText;
                        }
                        if (s.error) {
                            s.error.call(s.context, xhr, status, errMsg);
                        }
                        deferred.reject(xhr, 'error', errMsg);
                        if (g) {
                            $.event.trigger("ajaxError", [xhr, s, errMsg]);
                        }
                    }

                    if (g) {
                        $.event.trigger("ajaxComplete", [xhr, s]);
                    }

                    if (g && !--$.active) {
                        $.event.trigger("ajaxStop");
                    }

                    if (s.complete) {
                        s.complete.call(s.context, xhr, status);
                    }

                    callbackProcessed = true;
                    if (s.timeout) {
                        clearTimeout(timeoutHandle);
                    }

                    // clean up
                    setTimeout(function () {
                        if (!s.iframeTarget) {
                            $io.remove();
                        } else { //adding else to clean up existing iframe response.
                            $io.attr('src', s.iframeSrc);
                        }
                        xhr.responseXML = null;
                    }, 100);
                }

                var toXml = $.parseXML || function (s, doc) { // use parseXML if available (jQuery 1.5+)
                    if (window.ActiveXObject) {
                        doc = new ActiveXObject('Microsoft.XMLDOM');
                        doc.async = 'false';
                        doc.loadXML(s);
                    } else {
                        doc = (new DOMParser()).parseFromString(s, 'text/xml');
                    }
                    return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
                };
                var parseJSON = $.parseJSON || function (s) {
                    /*jslint evil:true */
                    return window['eval']('(' + s + ')');
                };

                var httpData = function (xhr, type, s) { // mostly lifted from jq1.4.4

                    var ct = xhr.getResponseHeader('content-type') || '',
                        xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
                        data = xml ? xhr.responseXML : xhr.responseText;

                    if (xml && data.documentElement.nodeName === 'parsererror') {
                        if ($.error) {
                            $.error('parsererror');
                        }
                    }
                    if (s && s.dataFilter) {
                        data = s.dataFilter(data, type);
                    }
                    if (typeof data === 'string') {
                        if (type === 'json' || !type && ct.indexOf('json') >= 0) {
                            data = parseJSON(data);
                        } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
                            $.globalEval(data);
                        }
                    }
                    return data;
                };

                return deferred;
            }
        };

        /**
         * ajaxForm() provides a mechanism for fully automating form submission.
         *
         * The advantages of using this method instead of ajaxSubmit() are:
         *
         * 1: This method will include coordinates for <input type="image" /> elements (if the element
         *    is used to submit the form).
         * 2. This method will include the submit element's name/value data (for the element that was
         *    used to submit the form).
         * 3. This method binds the submit() method to the form for you.
         *
         * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
         * passes the options argument along after properly binding events for submit elements and
         * the form itself.
         */
        $.fn.ajaxForm = function (options) {
            options = options || {};
            options.delegation = options.delegation && $.isFunction($.fn.on);

            // in jQuery 1.3+ we can fix mistakes with the ready state
            if (!options.delegation && this.length === 0) {
                var o = {s: this.selector, c: this.context};
                if (!$.isReady && o.s) {
                    log('DOM not ready, queuing ajaxForm');
                    $(function () {
                        $(o.s, o.c).ajaxForm(options);
                    });
                    return this;
                }
                // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
                log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
                return this;
            }

            if (options.delegation) {
                $(document)
                    .off('submit.form-plugin', this.selector, doAjaxSubmit)
                    .off('click.form-plugin', this.selector, captureSubmittingElement)
                    .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
                    .on('click.form-plugin', this.selector, options, captureSubmittingElement);
                return this;
            }

            return this.ajaxFormUnbind()
                .bind('submit.form-plugin', options, doAjaxSubmit)
                .bind('click.form-plugin', options, captureSubmittingElement);
        };

// private event handlers
        function doAjaxSubmit(e) {
            /*jshint validthis:true */
            var options = e.data;
            if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
                e.preventDefault();
                $(e.target).ajaxSubmit(options); // #365
            }
        }

        function captureSubmittingElement(e) {
            /*jshint validthis:true */
            var target = e.target;
            var $el = $(target);
            if (!($el.is("[type=submit],[type=image]"))) {
                // is this a child element of the submit el?  (ex: a span within a button)
                var t = $el.closest('[type=submit]');
                if (t.length === 0) {
                    return;
                }
                target = t[0];
            }
            var form = this;
            form.clk = target;
            if (target.type == 'image') {
                if (e.offsetX !== undefined) {
                    form.clk_x = e.offsetX;
                    form.clk_y = e.offsetY;
                } else if (typeof $.fn.offset == 'function') {
                    var offset = $el.offset();
                    form.clk_x = e.pageX - offset.left;
                    form.clk_y = e.pageY - offset.top;
                } else {
                    form.clk_x = e.pageX - target.offsetLeft;
                    form.clk_y = e.pageY - target.offsetTop;
                }
            }
            // clear form vars
            setTimeout(function () {
                form.clk = form.clk_x = form.clk_y = null;
            }, 100);
        }


// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
        $.fn.ajaxFormUnbind = function () {
            return this.unbind('submit.form-plugin click.form-plugin');
        };

        /**
         * formToArray() gathers form element data into an array of objects that can
         * be passed to any of the following ajax functions: $.get, $.post, or load.
         * Each object in the array has both a 'name' and 'value' property.  An example of
         * an array for a simple login form might be:
         *
         * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
         *
         * It is this array that is passed to pre-submit callback functions provided to the
         * ajaxSubmit() and ajaxForm() methods.
         */
        $.fn.formToArray = function (semantic, elements) {
            var a = [];
            if (this.length === 0) {
                return a;
            }

            var form = this[0];
            var formId = this.attr('id');
            var els = semantic ? form.getElementsByTagName('*') : form.elements;
            var els2;

            if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390
                els = $(els).get();  // convert to standard array
            }

            // #386; account for inputs outside the form which use the 'form' attribute
            if (formId) {
                els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
                if (els2.length) {
                    els = (els || []).concat(els2);
                }
            }

            if (!els || !els.length) {
                return a;
            }

            var i, j, n, v, el, max, jmax;
            for (i = 0, max = els.length; i < max; i++) {
                el = els[i];
                n = el.name;
                if (!n || el.disabled) {
                    continue;
                }

                if (semantic && form.clk && el.type == "image") {
                    // handle image inputs on the fly when semantic == true
                    if (form.clk == el) {
                        a.push({name: n, value: $(el).val(), type: el.type});
                        a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y});
                    }
                    continue;
                }

                v = $.fieldValue(el, true);
                if (v && v.constructor == Array) {
                    if (elements) {
                        elements.push(el);
                    }
                    for (j = 0, jmax = v.length; j < jmax; j++) {
                        a.push({name: n, value: v[j]});
                    }
                } else if (feature.fileapi && el.type == 'file') {
                    if (elements) {
                        elements.push(el);
                    }
                    var files = el.files;
                    if (files.length) {
                        for (j = 0; j < files.length; j++) {
                            a.push({name: n, value: files[j], type: el.type});
                        }
                    } else {
                        // #180
                        a.push({name: n, value: '', type: el.type});
                    }
                } else if (v !== null && typeof v != 'undefined') {
                    if (elements) {
                        elements.push(el);
                    }
                    a.push({name: n, value: v, type: el.type, required: el.required});
                }
            }

            if (!semantic && form.clk) {
                // input type=='image' are not found in elements array! handle it here
                var $input = $(form.clk), input = $input[0];
                n = input.name;
                if (n && !input.disabled && input.type == 'image') {
                    a.push({name: n, value: $input.val()});
                    a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y});
                }
            }
            return a;
        };

        /**
         * Serializes form data into a 'submittable' string. This method will return a string
         * in the format: name1=value1&amp;name2=value2
         */
        $.fn.formSerialize = function (semantic) {
            //hand off to jQuery.param for proper encoding
            return $.param(this.formToArray(semantic));
        };

        /**
         * Serializes all field elements in the jQuery object into a query string.
         * This method will return a string in the format: name1=value1&amp;name2=value2
         */
        $.fn.fieldSerialize = function (successful) {
            var a = [];
            this.each(function () {
                var n = this.name;
                if (!n) {
                    return;
                }
                var v = $.fieldValue(this, successful);
                if (v && v.constructor == Array) {
                    for (var i = 0, max = v.length; i < max; i++) {
                        a.push({name: n, value: v[i]});
                    }
                } else if (v !== null && typeof v != 'undefined') {
                    a.push({name: this.name, value: v});
                }
            });
            //hand off to jQuery.param for proper encoding
            return $.param(a);
        };

        /**
         * Returns the value(s) of the element in the matched set.  For example, consider the following form:
         *
         *  <form><fieldset>
         *      <input name="A" type="text" />
         *      <input name="A" type="text" />
         *      <input name="B" type="checkbox" value="B1" />
         *      <input name="B" type="checkbox" value="B2"/>
         *      <input name="C" type="radio" value="C1" />
         *      <input name="C" type="radio" value="C2" />
         *  </fieldset></form>
         *
         *  var v = $('input[type=text]').fieldValue();
         *  // if no values are entered into the text inputs
         *  v == ['','']
         *  // if values entered into the text inputs are 'foo' and 'bar'
         *  v == ['foo','bar']
         *
         *  var v = $('input[type=checkbox]').fieldValue();
         *  // if neither checkbox is checked
         *  v === undefined
         *  // if both checkboxes are checked
         *  v == ['B1', 'B2']
         *
         *  var v = $('input[type=radio]').fieldValue();
         *  // if neither radio is checked
         *  v === undefined
         *  // if first radio is checked
         *  v == ['C1']
         *
         * The successful argument controls whether or not the field element must be 'successful'
         * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
         * The default value of the successful argument is true.  If this value is false the value(s)
         * for each element is returned.
         *
         * Note: This method *always* returns an array.  If no valid value can be determined the
         *    array will be empty, otherwise it will contain one or more values.
         */
        $.fn.fieldValue = function (successful) {
            for (var val = [], i = 0, max = this.length; i < max; i++) {
                var el = this[i];
                var v = $.fieldValue(el, successful);
                if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
                    continue;
                }
                if (v.constructor == Array) {
                    $.merge(val, v);
                } else {
                    val.push(v);
                }
            }
            return val;
        };

        /**
         * Returns the value of the field element.
         */
        $.fieldValue = function (el, successful) {
            var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
            if (successful === undefined) {
                successful = true;
            }

            if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
                (t == 'checkbox' || t == 'radio') && !el.checked ||
                (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
                tag == 'select' && el.selectedIndex == -1)) {
                return null;
            }

            if (tag == 'select') {
                var index = el.selectedIndex;
                if (index < 0) {
                    return null;
                }
                var a = [], ops = el.options;
                var one = (t == 'select-one');
                var max = (one ? index + 1 : ops.length);
                for (var i = (one ? index : 0); i < max; i++) {
                    var op = ops[i];
                    if (op.selected) {
                        var v = op.value;
                        if (!v) { // extra pain for IE...
                            v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
                        }
                        if (one) {
                            return v;
                        }
                        a.push(v);
                    }
                }
                return a;
            }
            return $(el).val();
        };

        /**
         * Clears the form data.  Takes the following actions on the form's input fields:
         *  - input text fields will have their 'value' property set to the empty string
         *  - select elements will have their 'selectedIndex' property set to -1
         *  - checkbox and radio inputs will have their 'checked' property set to false
         *  - inputs of type submit, button, reset, and hidden will *not* be effected
         *  - button elements will *not* be effected
         */
        $.fn.clearForm = function (includeHidden) {
            return this.each(function () {
                $('input,select,textarea', this).clearFields(includeHidden);
            });
        };

        /**
         * Clears the selected form elements.
         */
        $.fn.clearFields = $.fn.clearInputs = function (includeHidden) {
            var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
            return this.each(function () {
                var t = this.type, tag = this.tagName.toLowerCase();
                if (re.test(t) || tag == 'textarea') {
                    this.value = '';
                } else if (t == 'checkbox' || t == 'radio') {
                    this.checked = false;
                } else if (tag == 'select') {
                    this.selectedIndex = -1;
                } else if (t == "file") {
                    if (/MSIE/.test(navigator.userAgent)) {
                        $(this).replaceWith($(this).clone(true));
                    } else {
                        $(this).val('');
                    }
                } else if (includeHidden) {
                    // includeHidden can be the value true, or it can be a selector string
                    // indicating a special test; for example:
                    //  $('#myForm').clearForm('.special:hidden')
                    // the above would clean hidden inputs that have the class of 'special'
                    if ((includeHidden === true && /hidden/.test(t)) ||
                        (typeof includeHidden == 'string' && $(this).is(includeHidden))) {
                        this.value = '';
                    }
                }
            });
        };

        /**
         * Resets the form data.  Causes all form elements to be reset to their original value.
         */
        $.fn.resetForm = function () {
            return this.each(function () {
                // guard against an input with the name of 'reset'
                // note that IE reports the reset function as an 'object'
                if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
                    this.reset();
                }
            });
        };

        /**
         * Enables or disables any matching elements.
         */
        $.fn.enable = function (b) {
            if (b === undefined) {
                b = true;
            }
            return this.each(function () {
                this.disabled = !b;
            });
        };

        /**
         * Checks/unchecks any matching checkboxes or radio buttons and
         * selects/deselects and matching option elements.
         */
        $.fn.selected = function (select) {
            if (select === undefined) {
                select = true;
            }
            return this.each(function () {
                var t = this.type;
                if (t == 'checkbox' || t == 'radio') {
                    this.checked = select;
                } else if (this.tagName.toLowerCase() == 'option') {
                    var $sel = $(this).parent('select');
                    if (select && $sel[0] && $sel[0].type == 'select-one') {
                        // deselect all other options
                        $sel.find('option').selected(false);
                    }
                    this.selected = select;
                }
            });
        };

// expose debug var
        $.fn.ajaxSubmit.debug = false;

// helper fn for console logging
        function log() {
            if (!$.fn.ajaxSubmit.debug) {
                return;
            }
            var msg = '[jquery.form] ' + Array.prototype.join.call(arguments, '');
            if (window.console && window.console.log) {
                window.console.log(msg);
            } else if (window.opera && window.opera.postError) {
                window.opera.postError(msg);
            }
        }

    }));


</script>

<script type="text/javascript">
    // $("#btn-reg").click(function() {
    // 	var user = $.trim($("#username").val());
    // 	var pwd = $.trim($("#ipwd").val());
    // 	if(user==""||pwd==""){
    // 		alert("请填写用户名和密码！");
    // 	}else{
    // 		$.ajax({
    //             "url" : "/users/reg",
    //             "data" : $("#form-reg").serialize(),
    //             "type" : "post",
    //             "dataType" : "json",
    //             "success" : function(json) {
    //                 if (json.state == 20) {
    //                     alert("注册成功，请登录！");
    //                     location.href = "login.html";
    //                 } else {
    //                     alert(json.message);
    //                 }
    //             }
    //         });
    // 	}
    // });
    $(function () {

        $("#registerForm").validate({
            rules: {
                username: {
                    required: true,
                    rangelength: [4, 16],
                    /* remote:{
                      url:"/checkUsername.do",
                      type:"post"
                    }*/
                },
                password: {
                    required: true,
                    rangelength: [4, 16]
                },
                confirmPwd: {
                    equalTo: "#password"
                }
            },
            messages: {
                username: {
                    required: "请输入用户名",
                    rangelength: "用户名的长度在{0}~{1}之间",
                    remote: "用户名已经存在!"
                },
                password: {
                    required: "请输入密码",
                    rangelength: "密码的长度在{0}~{1}之间"
                },
                confirmPwd: {
                    equalTo: "两次输入密码不同"
                }
            },
            submitHandler: function (form) {
                //使用ajax提交表单
                $("#registerForm").ajaxSubmit({
                    dataType: "json",
                    "success": function (json) {
                        if (json.state == 20) {
                            alert("注册成功，请登录！");
                            location.href = "login.html";
                        } else {
                            alert(json.message);
                        }
                    }
                });

            },
            errorClass: "text-danger",
            highlight: function (element, errorClass) {
                //给输入框添加红色外框
                $(element).closest("div.form-group").addClass("has-error");
            },
            unhighlight: function (element, errorClass) {
                $(element).closest("div.form-group").removeClass("has-error");
            }
        });
    })

</script>

</body>

</html>
